﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using CID.Domain.Development;
using CID.Domain.Infrastructure;
using CID.Domain.Operations;
using CID.Domain.Users;
using CID.Support.Exceptions;
using CID.Web.Models.Development;
using CID.Web.Support;
using CID.Web.Support.Ajax;
using NailsFramework.IoC;
using NailsFramework.Persistence;
using Environment = CID.Domain.Operations.Environment;
using Version = CID.Domain.Development.Version;

namespace CID.Web.Controllers
{
    [CIDAuthorize(UserRole.ComponentManager, UserRole.Manager, UserRole.Architect, UserRole.ReleaseManager)]
    public class ComponentController : AjaxEnabledBaseCRUDController<IComponent, Component, ComponentViewModel>
    {
        #region Users helper members
        [Inject]
        public IBag<User> Users { private get; set; }
        private IEnumerable<User> EnabledUsers { get { return Users.Where(x => x.Enabled); } }
        #endregion

        #region Server Roles helper members
        [Inject]
        public IBag<ServerRole> ServerRoles { private get; set; }
        private IEnumerable<ServerRole> EnabledServerRoles { get { return ServerRoles.Where(x => x.Enabled); } }
        #endregion

        #region ActivationTemplate helper members
        [Inject]
        public IBag<ActivationTemplate> ActivationTemplates { private get; set; }
        private IEnumerable<ActivationTemplate> EnabledActivationTemplates { get { return ActivationTemplates.Where(x => x.Enabled); } }
        #endregion

        #region DeactivationTemplate helper members
        [Inject]
        public IBag<DeactivationTemplate> DeactivationTemplates { private get; set; }
        private IEnumerable<DeactivationTemplate> EnabledDeactivationTemplates { get { return DeactivationTemplates.Where(x => x.Enabled); } }
        #endregion

        #region Environment helper members
        [Inject]
        public IBag<Environment> Environments { private get; set; }
        private IEnumerable<Environment> EnabledEnvironments { get { return Environments.Where(x => x.Enabled); } }
        #endregion

        protected override Component CreateModel(ComponentViewModel viewModel)
        {
            return new Component(viewModel.Name, viewModel.Description, ServerRoles.GetById(viewModel.RequiredServerRoleId), Users.GetById(viewModel.ResponsibleId));
        }

        protected override ComponentViewModel GetViewModel(int id)
        {
            var result = new ComponentViewModel(GetModel(id), EnabledEnvironments.ToArray());
            PrepareViewModelForView(result);
            return result;
        }

        protected override void PrepareViewModelForView(ComponentViewModel viewModel)
        {
            base.PrepareViewModelForView(viewModel);
            
            viewModel.SetAvailableServerRoles(EnabledServerRoles);
            viewModel.SetAvailableUsers(EnabledUsers);
            viewModel.SetAvailableActivationTemplates(EnabledActivationTemplates);
            viewModel.SetAvailableDeactivationTemplates(EnabledDeactivationTemplates);
            viewModel.SetAvailableEnvironments(EnabledEnvironments);

            if (viewModel.ResponsibleId.HasValue && String.IsNullOrEmpty(viewModel.Responsible))
                viewModel.Responsible = Users.GetById(viewModel.ResponsibleId.Value).Name;
            if (viewModel.RequiredServerRoleId.HasValue && String.IsNullOrEmpty(viewModel.RequiredServerRole))
                viewModel.RequiredServerRole = ServerRoles.GetById(viewModel.RequiredServerRoleId.Value).Name;
            if (viewModel.ActivationTemplateId.HasValue && (viewModel.ActivationTemplate == null))
                viewModel.ActivationTemplate = ActivationTemplates.GetById(viewModel.ActivationTemplateId.Value).Name;
            if (viewModel.DeactivationTemplateId.HasValue && (viewModel.DeactivationTemplate == null))
                viewModel.DeactivationTemplate = DeactivationTemplates.GetById(viewModel.DeactivationTemplateId.Value).Name;
        }

        #region DeploymentTemplate helper members
        [Inject]
        public IBag<DeploymentTemplate> DeploymentTemplates { private get; set; }
        private IEnumerable<DeploymentTemplate> EnabledDeploymentTemplates { get { return DeploymentTemplates.Where(x => x.Enabled); } }
        #endregion

        #region Release helper members
        [Inject]
        public IBag<Release> Releases { private get; set; }
        private IEnumerable<Release> EnabledReleases { get { return Releases.Where(x => ((x.Status == ReleaseStatus.Active) || (x.Status == ReleaseStatus.Planned))); } }
        #endregion

        #region Version helper members
        [Inject]
        public IBag<Version> VersionsBag { private get; set; }

        protected Version GetVersion(int id)
        {
            return VersionsBag.GetById(id);
        }
        #endregion

        #region Dependency components helper methods
        private IEnumerable<Component> GetAvailableDependencies(int ownerComponentId)
        {
            return this.Entities.Where(x => x.Enabled && (x.Id != (ownerComponentId)));
        }
        #endregion

        protected virtual void PrepareViewModelForView(VersionViewModel viewModel)
        {
            viewModel.SetAvailableDeploymentTemplates(EnabledDeploymentTemplates);
            viewModel.SetAvailableReleases(EnabledReleases);
            viewModel.SetAvailableEnvironments(EnabledEnvironments);
            viewModel.SetAvailableDependencies(GetAvailableDependencies(viewModel.ComponentId));
            viewModel.PostUrl = Url.RouteUrl(Url.RequestContext.RouteData.Values);

            if (viewModel.ReleaseId.HasValue && String.IsNullOrEmpty(viewModel.Release))
                viewModel.Release = Releases.GetById(viewModel.ReleaseId).Name;
            if (viewModel.DeploymentTemplateId.HasValue && String.IsNullOrEmpty(viewModel.DeploymentTemplate))
                viewModel.DeploymentTemplate = DeploymentTemplates.GetById(viewModel.DeploymentTemplateId.Value).Name;
        }

        protected virtual void PrepareViewModelForView(VersionViewModel viewModel, int originalId)
        {
            PrepareViewModelForView(viewModel);
            viewModel.PostUrl += String.Format("?originalId={0}", originalId);
        }

        public virtual ViewResult Versions(int id)
        {
            return View(GetModel(id));
        }

        public virtual ViewResult DetailsVersion(int id)
        {
            var viewModel = new VersionViewModel(GetVersion(id));
            return View(viewModel);
        }

        public virtual ViewResult CreateVersion(int id)
        {
            var component = GetModel(id);
            var viewModel = new VersionViewModel(component);
            PrepareViewModelForView(viewModel);
            return View(viewModel);
        }

        [HttpPost]
        public virtual ActionResult CreateVersion(VersionViewModel viewModel)
        {
            ActionResult result = null;
            //TODO: remove hardcode. This is needed since the CRUD operations of the Versions are embedded in the Component controller
            viewModel.Id = 0;
            PrepareViewModelForView(viewModel);
            if (!ModelState.IsValid) 
                result = View(viewModel);
            else{
                try
                {
                    var component = Entities.GetById(viewModel.ComponentId);
                    var version = component.CreateVersion(viewModel.Name, viewModel.Description);
                    viewModel.UpdateModel(version);
                    result = RedirectToAction("Versions", new { id = viewModel.ComponentId });
                }
                catch (DomainException e)
                {
                    CurrentUnitOfWork.Cancel();
                    ModelState.AddModelError("", e.Message);
                    result = View(viewModel);
                }
            }
            if(Request.IsAjaxRequest()) result = Json(AjaxActionResult.Create(result, Url));
            return result;
        }

        public virtual ViewResult CreateVersionCopy(int originalId)
        {
            var version = GetVersion(originalId);
            var viewModel = new VersionViewModel(version.CreateCopy());
            PrepareViewModelForView(viewModel, originalId);
            CurrentUnitOfWork.Cancel();
            return View("CreateVersion", viewModel);
        }

        [HttpPost]
        public virtual ActionResult CreateVersionCopy(int originalId, VersionViewModel viewModel)
        {
            ActionResult result = null;
            //TODO: remove hardcode. This is needed since the CRUD operations of the Versions are embedded in the Component controller
            viewModel.Id = 0;
            PrepareViewModelForView(viewModel);
            if (!ModelState.IsValid)
                result = View("CreateVersion", viewModel);
            else
            {
                try
                {
                    var version = GetVersion(originalId);
                    var versionCopy = version.CreateCopy();
                    viewModel.UpdateModel(versionCopy);
                    result = RedirectToAction("Versions", new { id = viewModel.ComponentId });
                }
                catch (DomainException e)
                {
                    CurrentUnitOfWork.Cancel();
                    ModelState.AddModelError("", e.Message);
                    result = View("CreateVersion", viewModel);
                }
            }
            if (Request.IsAjaxRequest()) result = Json(AjaxActionResult.Create(result, Url));
            return result;
        }

        public virtual ViewResult EditVersion(int id)
        {
            var viewModel = new VersionViewModel(GetVersion(id), EnabledEnvironments.ToArray());
            PrepareViewModelForView(viewModel);
            return View(viewModel);
        }

        [HttpPost]
        public virtual ActionResult EditVersion(int id, VersionViewModel viewModel)
        {
            ActionResult result = null;
            PrepareViewModelForView(viewModel);
            if (!ModelState.IsValid)
                result = View(viewModel);
            else
            {
                try
                {
                    var version = GetVersion(id);
                    viewModel.UpdateModel(version);
                    result = RedirectToAction("Versions", new { id = viewModel.ComponentId });
                }
                catch (DomainException e)
                {
                    CurrentUnitOfWork.Cancel();
                    ModelState.AddModelError("", e.Message);
                    result = View(viewModel);
                }
            }
            if (Request.IsAjaxRequest()) result = Json(AjaxActionResult.Create(result, Url));
            return result;
        }

        public virtual ViewResult DeleteVersion(int id)
        {
            var viewModel = new VersionViewModel(GetVersion(id));
            PrepareViewModelForView(viewModel);
            return View(viewModel);
        }

        [HttpPost]
        public virtual ActionResult DeleteVersion(int id, VersionViewModel viewModel)
        {
            var version = GetVersion(id);
            try
            {
                var component = version.Component;
                component.RemoveVersion(version);
                return RedirectToAction("Versions", new { id = component.Id });
            }
            catch (DomainException e)
            {
                ModelState.AddModelError("", e.Message);
                viewModel = new VersionViewModel(version);
                PrepareViewModelForView(viewModel);
                return View(viewModel);
            }
        }
    }
}
